package com.zkh.myframe.database;

import java.math.BigDecimal;
import java.sql.Connection;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;

import javax.sql.DataSource;

import com.zkh.myutils.database.data.QueryRunner;
import com.zkh.myutils.database.data.handler.ArrayListHandler;
import com.zkh.myutils.database.data.handler.BeanListHandler;
import com.zkh.myutils.database.data.handler.Handler;
import com.zkh.myutils.database.data.handler.MapListHandler;
import com.zkh.myutils.database.data.handler.ObjectHandler;
import com.zkh.myutils.database.dbpool.ConnectConfigure;
import com.zkh.myutils.database.dbpool.DBType;
import com.zkh.myutils.database.dbpool.DataSourceAdapter;
import com.zkh.myutils.database.dbpool.DbPoolDataSource;
import com.zkh.myutils.database.enums.JoinType;
import com.zkh.myutils.database.enums.Symbol;
import com.zkh.myutils.database.helper.ConditionPart;
import com.zkh.myutils.database.helper.TableHelper;
import com.zkh.myutils.page.PageUtil;
import com.zkh.myutils.utils.Assert;
import com.zkh.myutils.utils.Execute;
import com.zkh.myframe.annotation.Table;
import com.zkh.myutils.database.DatabaseException;
import com.zkh.myutils.database.conutils.PoolUtils;
import com.zkh.myutils.utils.StringUtils;

/**
 * 数据库操作模板类，目前兼容MySQL、SqlServer和Oracle。<br />
 * 本类支持分页处理，批处理操作，配合内置切面可支持事务。<br />
 * <b>注：addLimit()方法不仅对MySQL有效，对SqlServer和Oracle同样有效。<br />
 * 内置数据源和数据连接可通过PoolUtils工具类获取。</b><br /><br />
 * 实例化方法：<br />&nbsp;&nbsp;
 * 	<b><code>DataTemplate template = new DataTemplate();</code></b><br /><br /><b>
 * 本类支持链式查询，如以下方法：<br />&nbsp;&nbsp;
 * 	<code>template.addTable("t_user").addLimit(5).doSelect();</code><br />
 * 也支持分散查询，如以下方法：<br />&nbsp;&nbsp;&nbsp;
 *  <code>
 *  template.addTable("t_user");<br />&nbsp;
 *  template.addLimit(5);<br />&nbsp;
 *  template.doSelect();</code><br />
 *  两种查询方式的效果完全相同。<br /><br />
 *  注意：每当调用doXXX()方法时，系统都将清除addXXXX()设置的值。只有调用doSelectCount()时，可通过参数设置不清除addXXXX()设置的值。
 * </b>
 * @author zkh
 *
 */
public class DataTemplate {
	
	//数据表对象
	private TableHelper helper = new TableHelper(ConnectConfigure.getDefaultSourceConfig().getDbtype());
	//批量参数
	private Object[][] batchParams;
	//queryString
	private String queryString;
	//表前缀
	private String tablePrefix;
	//释放句柄
	private Execute releaseHandler = ()->{};
	//数据源
	DataSource ds = PoolUtils.getDataSource();
	
	/**
	 * 数据操作对象
	 */
	private QueryRunner qr = new QueryRunner(ds);
	
	/**
	 * 无参构造
	 */
	public DataTemplate() {
	}
	
	/**
	 * 构造方法
	 * @param releaseHandler 释放句柄。该参数是为单例模式而设计的，外层使用ThreadLocal对象来存放DataTemplate，就需要使用ThreadLocal.remove()来关闭。这里的释放句柄，就是一个remove()操作，通过clearAll()方法来调用
	 */
	public DataTemplate(Execute releaseHandler) {
		this.releaseHandler = releaseHandler;
	}
	
	/**
	 * 设置数据源。非必须，不设置则使用默认数据源
	 * @param datasourceKey 数据源唯一标识key
	 * @return 返回当前实例，实现链式操作
	 */
	public DataTemplate setDataSource(String datasourceKey) {
		//获取数据源
		DbPoolDataSource dataSource = DataSourceAdapter.getInstanceOfDataSource(datasourceKey);
		//数据类型
		helper.setDBType(dataSource.getDataBaseInfo().getDbtype());
		//重置QueryRunner
		qr = new QueryRunner(ds = dataSource);
		//返回当前
		return this;
	}
	
	/**
	 * 设置表前缀，用以访问其它数据库或用户库
	 * @param tablePrefix 表前缀
	 * @return
	 */
	public DataTemplate setTablePrefix(String tablePrefix) {
		this.tablePrefix = tablePrefix;
		//返回当前
		return this;
	}

	/**
	 * 添加首张数据表名，可设置别名。如果是多表联表查询，从第二张表开始必须指定联表类型和ON条件<b><i>仅供非实体类查询时使用</i></b><br /><br />
	 * <b>特别提醒：如果此处添加了别名，查询列和条件中均可直接使用别名。</b>
	 * @param tablename 表名，可包含别名
	 * 
	 * @return 返回当前实例，实现链式操作
	 * @see #addTable(String tablename, JoinType type, String onLeft, String onRight, String... onLeftRight)
	 * @see #addTable(String tablename, JoinType type, ConditionPart part1, ConditionPart... conditionParts)
	 */
	public DataTemplate addTable(String tablename){
		//添加表
		helper.addTable(renderTable(tablename));
		return this;
	}
	
	/**
	 * 联表查询从第二张表开始添加表，ON条件字段一律用Symbol.EQ连接，条件之间用AND连接。
	 * @param tablename 表名，可包含别名，如：table2 as t2
	 * @param type 联表类型，通过枚举类获取，不能为空
	 * @param onLeft 第一个ON条件左边。如t1.id
	 * @param onRight 第一个ON条件右边。如t2.table1Id。（合并onLeft后即为t1.id=t2.table1Id）
	 * @param onLeftRight ON条件超过1个时，从第二ON条件开始，依次设置on2Left，on2Right，on3Left，on3Right...，两个参数为一组
	 * @return
	 */
	public DataTemplate addTable(String tablename, JoinType type, String onLeft, String onRight, String... onLeftRights) {
		helper.addTable(renderTable(tablename), type, onLeft, onRight, onLeftRights);
		return this;
	}
	
	/**
	 * 联表查询从第二张表开始添加表，可以实现任意复杂ON条件
	 * @param tablename 表名
	 * @param type 联表类型
	 * @param part1 第一个ON条件，不能为空
	 * @param conditionParts 更多联表条件
	 * @return
	 */
	public DataTemplate addTable(String tablename, JoinType type, ConditionPart part1, ConditionPart... conditionParts) {
		helper.addTable(renderTable(tablename), type, part1, conditionParts);
		return this;
	}
	
	/**
	 * 添加表名。只针对单表查询
	 * @param clazz 实体类Class，实体必须配置Table注解
	 * @return 返回当前实例，实现链式操作
	 */
	public <T> DataTemplate addTable(Class<T> clazz) {
		return addTable(clazz.getAnnotation(Table.class).name());
	}
	
	/**
	 * 设置查询列，每项为一个查询字段，可以包含别名。可多次调用，依次追加
	 * @param fields 查询列，可包含别名
	 * @return 返回当前实例，实现链式操作
	 */
	public DataTemplate addColumn(String... fields){
		helper.addColumn(fields);
		return this;
	}
	
	/**
	 * 设置操作列。包含insert和update中的列。<br/>
	 * <b>该方法专为批量操作设计，因为批量操作只需要字段名，参数需要单独传递，此处所有值都会默认为null。如果sql中值为函数，要单独调用addOperColumn(String field, Object value, boolean placeholder)来设置</b>
	 * @param fields 字段名。<b>注：每项必须是String类型，Object是方便List转数组Object[]后不用强转为String[]</b>
	 * @return 返回当前实例，实现链式操作
	 */
	public DataTemplate addOperColumn(Object... fields) {
		for(Object f: fields) helper.addOperColumn((String) f);
		return this;
	}
	
	/**
	 * 设置操作列。包含insert和update中的列，默认使用占位符
	 * @param field 字段名 
	 * @param value 字段值
	 * @return 返回当前实例，实现链式操作
	 */
	public DataTemplate addOperColumn(String field, Object value) {
		return addOperColumn(field, value, true);
	}
	
	/**
	 * 设置操作列。包含insert和update中的列
	 * @param field 字段名 
	 * @param value 字段值
	 * @param placeholder 是否使用占位符，为特殊值设置，如设置：count=count+1，就不能使用占位符
	 * @return 返回当前实例，实现链式操作
	 */
	public DataTemplate addOperColumn(String field, Object value, boolean placeholder) {
		helper.addOperColumn(field, value, placeholder);
		//返回对象本身，以实现链式操作
		return this;
	}
	
	/**
	 * 批量添加操作字段，全部使用占位符
	 * @param fieldValues 操作字段和值，格式为[字段名, 字段值, 字段名, 字段值...]，依此类推，其中字段名必须是字符串
	 * @return 返回当前实例，实现链式操作
	 */
	public DataTemplate addOperColumnValues(Object... fieldValues) {
		//遍历赋值
		for(int i=1,len = fieldValues.length; i<len; i+=2) {
			helper.addOperColumn((String) fieldValues[i-1], fieldValues[i], true);
		}
		//返回对象本身，以实现链式操作
		return this;
	}
	
	/**
	 * 添加where条件。默认比较符为=，与上一个条件的逻辑符为AND<br />
	 * 注：批处理操作和普通的操作使用的参数不是同一个变量，相互不影响。
	 * @param field 键
	 * @param value 值
	 * @return 返回当前实例，实现链式操作
	 */
	public DataTemplate addCondition(String field, Object value) {
		return addCondition(field, Symbol.EQ, value, true);
	}
	
	/**
	 * 添加where条件。默认比较符为=，与上一个条件的逻辑符为AND<br />
	 * 注：批处理操作和普通的操作使用的参数不是同一个变量，相互不影响。
	 * @param field 键
	 * @param value 值
	 * @param placeholder 是否使用占位符?
	 * @return 返回当前实例，实现链式操作
	 */
	public DataTemplate addCondition(String field, Object value, boolean placeholder) {
		return addCondition(field, Symbol.EQ, value, placeholder);
	}
	
	/**
	 * 添加where条件。默认与上一个条件的逻辑符为AND<br />
	 * 注：批处理操作和普通的操作使用的参数不是同一个变量，相互不影响。
	 * @param field 键
	 * @param sign 比较符
	 * @param value 值
	 * @return 返回当前实例，实现链式操作
	 */
	public DataTemplate addCondition(String field, Symbol sign, Object value) {
		return addCondition(field, sign, value, true);
	}
	
	/**
	 * 添加where条件。<br />
	 * 注：批处理操作和普通的操作使用的参数不是同一个变量，相互不影响。
	 * @param field 键
	 * @param sign 比较符
	 * @param value 值
	 * @param placeholder 是否占位
	 * @return 返回当前实例，实现链式操作
	 */
	public DataTemplate addCondition(String field, Symbol sign, Object value, boolean placeholder) {
		return addCondition(field, sign, value, Symbol.AND, placeholder);
	}
	
	/**
	 * 添加where条件。<br />
	 * 注：批处理操作和普通的操作使用的参数不是同一个变量，相互不影响。
	 * @param field 键
	 * @param sign 比较符
	 * @param value 值
	 * @param logic 与前一个条件之间的逻辑
	 * @param placeholder 是否占位
	 * @return 返回当前实例，实现链式操作
	 */
	public DataTemplate addCondition(String field, Symbol sign, Object value, Symbol logic, boolean placeholder) {
		helper.addCondition(field, sign, value, logic, placeholder);
		//返回对象本身，以实现链式操作
		return this;
	}
	
	/**
	 * 添加where条件块
	 * @param part 条件块
	 * @return
	 */
	public DataTemplate addConditionPart(ConditionPart part) {
		helper.addConditionPart(part);
		return this;
	}
	
	/**
	 * 添加having条件。默认等于，且与上一个条件的逻辑连接符为and
	 * @param field 键
	 * @param sign 比较符
	 * @param value 值
	 * @return 返回当前实例，实现链式操作
	 */
	public DataTemplate addHaving(String field, Object value) {
		return addHaving(field, Symbol.EQ, value, true);
	}
	
	/**
	 * 添加having条件。默认与上一个条件的逻辑连接符为and
	 * @param field 键
	 * @param sign 比较符
	 * @param value 值
	 * @return 返回当前实例，实现链式操作
	 */
	public DataTemplate addHaving(String field, Symbol sign, Object value) {
		return addHaving(field, sign, value, true);
	}
	
	/**
	 * 添加having条件。<br />
	 * 注：批处理操作和普通的操作使用的参数不是同一个变量，相互不影响。
	 * @param field 键
	 * @param sign 比较符
	 * @param value 值
	 * @param placeholder 是否占位
	 * @return 返回当前实例，实现链式操作
	 */
	public DataTemplate addHaving(String field, Symbol sign, Object value, boolean placeholder) {
		return addHaving(field, sign, value, Symbol.AND, placeholder);
	}
	
	/**
	 * 添加having条件
	 * @param field 键
	 * @param sign 比较符
	 * @param value 值
	 * @param logic 与前一个条件之间的逻辑
	 * @param placeholder 是否占位
	 * @return 返回当前实例，实现链式操作
	 */
	public DataTemplate addHaving(String field, Symbol sign, Object value, Symbol logic, boolean placeholder) {
		helper.addHaving(field, sign, value, logic, placeholder);
		//返回对象本身，以实现链式操作
		return this;
	}

	/**
	 * 添加having条件块
	 * @param part 条件块
	 * @return
	 */
	public DataTemplate addHavingPart(ConditionPart part) {
		helper.addHavingPart(part);
		return this;
	}

	/**
	 * 分组，分组操作一般是非实体类查询<br />
	 * <b>多次调用依次追加设置值</b>
	 * @param groups 分组列名
	 * @return 返回当前实例，实现链式操作
	 */
	public DataTemplate addGroup(String... groups){
		helper.addGroup(groups);
		return this;
	}
	
	/**
	 * 设置排序字段
	 * @param orderFields 排序字段，全部正序
	 * @return 返回当前实例，实现链式操作
	 */
	public DataTemplate addOrder(String... orderFields) {
		helper.addOrder(orderFields);
		//返回对象本身，以实现链式操作
		return this;
	}
	
	/**
	 * 设置排序字段。排序方式为：ASC/DESC
	 * @param orderSort 排序字段和方式，依次为：字段1、排序方式、字段2、排序方式、字段3、排序方式。。。
	 * @return 返回当前实例，实现链式操作
	 */
	public DataTemplate addOrderSort(String... orderSort) {
		helper.addOrderSort(orderSort);
		//返回对象本身，以实现链式操作
		return this;
	}
	
	/**
	 * 设置获取条数。多次调用取最后一次的值<br />
	 * @param limit 获取条数
	 * @return 返回当前实例，实现链式操作
	 */
	public DataTemplate addLimit(int limit){
		helper.setLimit(new int[]{limit});
		return this;
	}
	
	/**
	 * 设置获取条数
	 * @param start 开始索引（从0开始）
	 * @param limit 获取条数
	 * @return 返回当前实例，实现链式操作
	 */
	public DataTemplate addLimit(int start, int limit){
		helper.setLimit(new int[]{start, limit});
		return this;
	}
	
	/**
	 * 添加分页
	 * @param pageIndex 当前页码。如果为null或小于1，则重置为1
	 * @param pageSize 每页数据条数。如果为null或小于1，则重置为10，如果大于1000，则重置为1000
	 * @return 返回当前实例，实现链式操作
	 */
	public DataTemplate addPage(Integer pageIndex, Integer pageSize){
		//保存当前页和每页显示条数
		helper.setPage(pageIndex, pageSize);
		return this;
	}
	
	/**
	 * 设置queryString，辅助生成分页信息
	 * @param queryString 查询字符串，通过request.getQueryString()获取
	 * @return 返回当前实例，实现链式操作
	 */
	public DataTemplate addQueryString(String queryString) {
		this.queryString = queryString;return this;
	}
	
	/**
	 * 批量添加参数，<b>仅用作批处理操作时的参数。</b>批量操作实体类无需设置该项。
	 * @param params 批处理参数，外层数组代表批次，内层数组才是真正的参数
	 * @return 返回当前实例，实现链式操作
	 */
	public DataTemplate addBatchParams(Object[][] params){
		this.batchParams = params;
		return this;
	}
	
	/**
	 * 批量添加参数，<b>仅用作批处理操作时的参数。</b>批量操作实体类无需设置该项。
	 * @param paramList 批处理参数，外层集合代表批次，内层数组才是真正的参数
	 * @return 返回当前实例，实现链式操作
	 */
	public DataTemplate addBatchParams(List<Object[]> paramList){
		//长度
		int len = paramList.size();
		//参数容器
		Object[][] params = new Object[len][];
		//遍历
		for(int i=0;i<len;i++) {
			params[i] = paramList.get(i);
		}
		return addBatchParams(params);
	}
	
	/************  查询操作   **********/
	/**
	 * 查询一条数据。如果已显式指定表名，则clazz的作用仅为标识返回类型。如果没有指定表名，则会从Table注解中获取表名。
	 * @param clazz 实体类class属性仅用作指定返回类型
	 * @return 返回泛型实体
	 */
	public <T> T doSelectOne(Class<T> clazz){
		//检查条件
		addLimit(1);
		//查询
		List<T> list = doSelect(clazz);
		//查看是否有数据
		return list.size()==0 ? null : list.get(0);
	}
	
	/**
	 * 查询集合。如果已显式指定表名，则clazz的作用仅为标识返回类型。如果没有指定表名，则会从Table注解中获取表名。
	 * @param clazz 实体类class属性仅用作指定返回类型
	 * @return 返回泛型实体集合
	 */
	public <T> List<T> doSelect(Class<T> clazz){
		//检查表名
		checkTable(clazz);
		//查询
		return doQuery(PoolUtils.getTransactionConnection(), helper.getSelectSql(), new BeanListHandler<>(clazz), helper.getParamList().toArray());
	}
	
	/**
	 * 分页查询<br />
	 * <b>注意：<br />
	 * 	1、泛型T可为实体类，也可为Object[]和Map类型。如：Object[].class, Map.class<br />&nbsp;&nbsp;
	 * 参数为Map.class的，泛型T的原始类型均为 Map&lt;String, Object&gt;<br />
	 *  2、当泛型T不是实体类类型时，查询前需先设置表名</b>
	 * @param clazz 对象class属性
	 * @return 返回泛型实体分页对象
	 */
	@SuppressWarnings("unchecked")
	public <T> PageUtil<T> doSelectPage(Class<T> clazz){
		//检查表名
		checkTable(clazz);
		//查询totalItems
		long totalItems = doSelectCount(false);
		//分页信息
		int[] page = helper.getPage();
		//创建分页类(page={pageIndex, pageSize})
		PageUtil<T> pages = new PageUtil<T>(page[1], page[0], totalItems);
		//列表
		List<T> list;
		//查询数据
		if(totalItems==0) {
			list = new ArrayList<>();
			//手动清除环境
			clearAll();
		}else {
			list = clazz.isArray() ? (List<T>) doSelectArray() : ("interface java.util.Map".equals(clazz.toString()) ? (List<T>) doSelectMap() : doSelect(clazz));
		}
		//设置到分页对象
		pages.setDataList(list);
		//组装分页信息
		if(queryString!=null){
			pages.dealPageinfo(queryString);
		}
		//返回分页类
		return pages;
	}
	
	/**
	 * 分页查询数据。传入一个正常不分页的查询语句，该方法自动分页。<br>
	 * 注：调用该方法前，请先调用addPage()设置分页参数，addQueryString()方法视需要调用
	 * @param clazz 查询对象
	 * @param sql SQL语句
	 * @param params 参数
	 * @return 返回泛型实体分页对象
	 */
	@SuppressWarnings("unchecked")
	public <T> PageUtil<T> doSelectPage(Class<T> clazz, String sql, Object... params){
		//查询单行单列数据
		long totalItems = Long.parseLong(doQueryNoClear(PoolUtils.getTransactionConnection(), "select count(1) from (" + sql.replaceAll("(?i)\\s*order\\s+by[^)]+$", "") + ") t", new ObjectHandler<>(), params).toString());
		//分页信息
		int[] page = helper.getPage();
		//预处理limit和page
		int[] limits = new int[] {(page[0]-1)*page[1], page[1]};
		//创建分页类(page={pageIndex, pageSize})
		PageUtil<T> pages = new PageUtil<T>(page[1], page[0], totalItems);
		/** 改造SQL，使用SQL能够分页 **/
		//满一页，才进行分页
		if(totalItems>page[1]) {
			//MySQL
			if(DBType.TYPE_MYSQL==helper.getDbType()) {
				sql += " limit " + limits[0] + "," + limits[1];
			}//oracle
			else if(DBType.TYPE_ORACLE==helper.getDbType()){
				sql = "SELECT * FROM (SELECT t.*, ROWNUM AS ROW_ID FROM (" + sql + ") t WHERE ROWNUM<="+ (limits[0] + limits[1]) + ") WHERE ROW_ID>" + limits[0];
			}//sqlserver
			else if(DBType.TYPE_SQLSERVER==helper.getDbType()){
				//插入top语句
				sql = sql.substring(0, 6) + " top " + (limits[0] + limits[1]) + sql.substring(6);
				//拼接sql
				sql = "select * from (select t.*, row_number() over (order by (select 1)) ROW_ID from (" + sql + ") t) t where ROW_ID>" + limits[0];
			}
		}
		//数据容器
		List<T> list;
		//分别查询
		if(totalItems==0) {
			list = new ArrayList<T>();
		}else if(clazz.isArray()) {
			list = (List<T>) doQuery(PoolUtils.getTransactionConnection(), sql, new ArrayListHandler(), params);
		}else if("interface java.util.Map".equals(clazz.toString())) {
			list = (List<T>) doQuery(PoolUtils.getTransactionConnection(), sql, new MapListHandler(), params);
		}else {
			list = doQuery(PoolUtils.getTransactionConnection(), sql, new BeanListHandler<>(clazz), params);
		}
		//设置到分页对象
		pages.setDataList(list);
		//组装分页信息
		Assert.doIfNotEmpty(queryString, e->pages.dealPageinfo(e));
		//返回分页类
		return pages;
	}
	
	/**
	 * 查询Map集合，非实体类查询调用，必须调用addTable方法指定表名
	 * @return 返回Map集合
	 */
	public List<Map<String, Object>> doSelectMap(){
		//返回查询结果
		return doQuery(PoolUtils.getTransactionConnection(), helper.getSelectSql(), new MapListHandler(), helper.getParamList().toArray());
	}
	
	/**
	 * 查询数组集合，非实体类查询调用，必须调用addTable方法指定表名
	 * @return 返回数组集合
	 */
	public List<Object[]> doSelectArray(){
		//返回查询结果
		return doQuery(PoolUtils.getTransactionConnection(), helper.getSelectSql(), new ArrayListHandler(), helper.getParamList().toArray());
	}
	
	/**
	 * 查询单列集合，非实体类查询调用，必须调用addTable方法指定表名。<br>
	 * <b>注：参数类型以JDBC默认转换类型为准。如数据库类型为date，JDBC默认以java.util.Date接收，所以泛型参数需传入Date.class，其它类型可能会引起类型转换失败异常。</b>
	 * @param <T> 列类型
	 * @param cls 返回类型的class
	 * @return 所有第一列组成的集合
	 */
	@SuppressWarnings("unchecked")
	public <T> List<T> doSelectColumnList(Class<T> cls){
		return doSelectColumnList(cls, e->(T) e);
	}
	
	/**
	 * 查询单列集合，非实体类查询调用，必须调用addTable方法指定表名。
	 * @param <T> 列类型
	 * @param cls 返回类型的class
	 * @param fun 自定义处理类型，开发者自己处理类型转换逻辑
	 * @return 所有第一列组成的集合
	 */
	public <T> List<T> doSelectColumnList(Class<T> cls, Function<Object, T> fun){
		return doSelectArray().stream().map(e->fun.apply(e[0])).collect(Collectors.toList());
	}
	
	/**
	 * 查询条数。查询后自动清除环境
	 * @return 返回查询数量
	 */
	public long doSelectCount(){
		return doSelectCount(true);
	}
	
	/**
	 * 查询条数
	 * @param clearAll 执行完成后，是否清除环境
	 * @return 返回查询数量
	 */
	public long doSelectCount(boolean clearAll){
		//SQL参数
		String sql = helper.getCountSql(); Object[] params = helper.getParamList().toArray();
		ObjectHandler<Object> handler = new ObjectHandler<>();
		//连接
		Connection conn = PoolUtils.getTransactionConnection();
		//查询单行单列数据
		Object res = Assert.getIfElse(conn==null, ()->qr.query(sql, handler, params), ()->qr.query(conn, sql, handler, params));
		//清除
		if(clearAll) {
			clearAll();
		}
		//总条数
		return Long.parseLong(res.toString());
	}
	
	/**
	 * 公共查询操作，执行后清除所有设置的环境，可直接调用。优先使用事务连接（如果有）
	 * @param sql SQL语句
	 * @param handler 处理数据对象
	 * @param params 参数
	 */
	public <T> T doQuery(String sql, Handler<T> handler, Object... params) {
		return doQuery(PoolUtils.getTransactionConnection(), sql, handler, params);
	}
	
	/**
	 * 公共查询操作，执行后清除所有设置的环境，可直接调用
	 * @param conn 事务连接，有则使用
	 * @param sql SQL语句
	 * @param handler 处理数据对象
	 * @param params 参数
	 */
	public <T> T doQuery(Connection conn, String sql, Handler<T> handler, Object... params) {
		try {
			return Assert.getIfElse(conn==null, ()->qr.query(sql, handler, params), ()->qr.query(conn, sql, handler, params));
		}finally {
			clearAll();
		}
	}
	
	/**
	 * 公共查询操作，执行后不清除设置的环境，可直接调用
	 * @param conn 事务连接，有则使用
	 * @param sql SQL语句
	 * @param handler 处理数据对象
	 * @param params 参数
	 */
	public <T> T doQueryNoClear(Connection conn, String sql, Handler<T> handler, Object... params) {
		return Assert.getIfElse(conn==null, ()->qr.query(sql, handler, params), ()->qr.query(conn, sql, handler, params));
	}
	
	
	/************  增、删、改操作   **********/
	
	/**
	 * 插入数据，返回自增主键
	 */
	public BigDecimal doInsert(){
		try {
			//获取事务连接
			Connection conn = PoolUtils.getTransactionConnection();
			//存在
			if(conn==null) {
				return qr.insert(helper.getInsertSQL(), helper.getParamList().toArray());
			}else {
				return qr.insert(conn, helper.getInsertSQL(), helper.getParamList().toArray());
			}
		} finally {
			clearAll();
		}
	}

	/**
	 * 插入数据，返回受影响行数
	 */
	public int doAdd(){
		return doExecute(PoolUtils.getTransactionConnection(), helper.getInsertSQL(), helper.getParamList().toArray());
	}

	/**
	 * 执行更新操作
	 */
	public int doUpdate(){
		return doExecute(PoolUtils.getTransactionConnection(), helper.getUpdateSQL(), helper.getParamList().toArray());
	}

	/**
	 * 执行删除操作。整表删除，请添加{@code where 1=1}条件
	 */
	public int doDelete(){
		return doExecute(PoolUtils.getTransactionConnection(), helper.getDeleteSQL(), helper.getParamList().toArray());
	}
	
	/**
	 * 公共执行操作，可直接调用。优先使用事务连接（如果有）
	 * @param sql SQL语句
	 * @param params 参数
	 */
	public int doExecute(String sql, Object... params) {
		return doExecute(PoolUtils.getTransactionConnection(), sql, params);
	}
	
	/**
	 * 公共执行操作，可直接调用
	 * @param conn 事务连接，有则使用
	 * @param sql SQL语句
	 * @param params 参数
	 */
	public int doExecute(Connection conn, String sql, Object... params) {
		try {
			return Assert.getIfElse(conn==null, ()->qr.update(sql, params), ()->qr.update(conn, sql, params));
		}finally {
			clearAll();
		}
	}
	
	/**
	 * 批量添加，返回自增主键数组，没有自增主键，数组元素值为null
	 */
	public BigDecimal[] doBatchInsert() {
		//判断参数为空
		Assert.ifTrueThrow(batchParams==null, "请先调用addBatchParams()方法添加批量参数", DatabaseException.class);
		//执行
		return doBatchInsert(PoolUtils.getTransactionConnection(), helper.getInsertSQL(), batchParams);
	}
	
	/**
	 * 批量添加，返回自增主键数组，没有自增主键，数组元素值为null
	 * @param conn 连接对象
	 * @param sql sql语句
	 * @param params 批量参数
	 * @return
	 */
	public BigDecimal[] doBatchInsert(Connection conn, String sql, Object[][] params) {
		try {
			return Assert.getIfElse(conn==null, ()->qr.batchInsert(sql, params), ()->qr.batchInsertTransaction(conn, sql, params));
		}finally {
			clearAll();
		}
	}
	
	/**
	 * 批量添加，返回受影响行数
	 */
	public int[] doBatchAdd() {
		//判断参数为空
		Assert.ifTrueThrow(batchParams==null, "请先调用addBatchParams()方法添加批量参数", DatabaseException.class);
		//执行
		return doBatch(helper.getInsertSQL(), batchParams);
	}
	
	/**
	 * 批量更新，返回受影响行数
	 */
	public int[] doBatchUpdate() {
		//判断参数为空
		Assert.ifTrueThrow(batchParams==null, "请先调用addBatchParams()方法添加批量参数", DatabaseException.class);
		//执行
		return doBatch(helper.getUpdateSQL(), batchParams);
	}
	
	/**
	 * 批量删除
	 * @param paramss 批量参数
	 */
	public int[] doBatchDelete() {
		//判断参数为空
		Assert.ifTrueThrow(batchParams==null, "请先调用addBatchParams()方法添加批量参数", DatabaseException.class);
		//执行
		return doBatch(helper.getDeleteSQL(), batchParams);
	}
	
	/**
	 * 批量操作。优先使用事务连接（如果有）
	 * @param sql SQL语句
	 * @param params 参数
	 */
	public int[] doBatch(String sql, Object[][] params) {
		return doBatch(PoolUtils.getTransactionConnection(), sql, params);
	}
	
	/**
	 * 批量操作
	 * @param conn 事务连接，有则使用
	 * @param sql SQL语句
	 * @param params 参数
	 */
	public int[] doBatch(Connection conn, String sql, Object[][] params) {
		try {
			return Assert.getIfElse(conn==null, ()->qr.batch(sql, params), ()->qr.batchTransaction(conn, sql, params));
		}finally {
			clearAll();
		}
	}
	
	/**
	 * 获取数据库类型
	 */
	public DBType getDbType() {
		return helper.getDbType();
	}
	
	/**
	 * 清除所有已设置数据。相当于一个全新的TableHelper实例
	 */
	public void clearAll() {
		//清除数据
		helper.clearAll(); batchParams = null; queryString = null; tablePrefix = null;
		//释放句柄
		releaseHandler.apply();
	}
	
	/**
	 * 查询数据前检查表名。如果不存在表名，则添加，且如果没有列，设置查询列为*
	 * @param clazz 查询类
	 */
	public <T> void checkTable(Class<T> clazz) {
		//表注解
		Table table;
		//表名为空
		if(helper.isTableEmpty() && (table = clazz.getAnnotation(Table.class))!=null) {
			//添加表名
			Assert.doIfElse(helper.isColumnEmpty(), ()->{
				//查询列为空，添加表别名
				addColumn("t.*");
				addTable(table.name() + " t");
			}, ()->helper.addTable(table.name()));
		}
	}
	
	/**
	 * 获取表前缀，用以访问其它数据库或用户库
	 * @param tablePrefix 表前缀
	 * @return
	 */
	String getTablePrefix() {
		return tablePrefix;
	}
	
	/**
	 * 拼接表名
	 * @param tablename 参数表名
	 * @return
	 */
	private String renderTable(String tablename) {
		return StringUtils.isEmpty(tablePrefix) ? tablename : tablePrefix + "." + tablename;
	}
}
